(function(){
	var Collections = require('../Collections.js'),
	    StringUtils = require('../StringUtils.js');
	
	var iterate = Collections.iterate;
	var _ = StringUtils.format;
	
	var _services = {};
	
	var describe = function(name){
		var service = _services[name];
		if( !service ){
			return {error:_('Service \'${name}\' does not exist.', {name:name})};
		}
		var endpoints = {};
		for(member in service){
			if( typeof service[member] === 'function' && !member.match(/^_.*/) )
				endpoints[member] = {
					__serviceName:name
					,__methodName:member
				};
			else if( !member.match(/^_.*/) ){
				endpoints[member] = service[member];
			}
		}
		return endpoints;
	}
	
	/**
	 * buildCallbackHandler(index, onResponse) : function()
	 *   constructs a delegate to be called when a response is ready.
	 */
	var buildCallbackHandler = function(index, onResponse){
		return function(){
			var _args = [];
			iterate(arguments, function(value){
				if( value && value.__alternate )
					_args.push({
						__serviceName : value.__alternate
					});
				else
					_args.push(value);
			});
			onResponse({callback:{parameters:_args, index:index}});
		};
	}
	
	/**
	 * buildCallbackHandlers(args, onResponse) : void
	 *   Iterates over the parameters passed in to the remote call and identifies
	 *   any functions and replaces them with delegates that will deliver a
	 *   response to the client
	 */
	var buildCallbackHandlers = function(args, onResponse){
		var hasCallback = false;
		iterate(args, function(item, index){
			if( item === '__function'){
				args[index] = buildCallbackHandler(index, onResponse);
				hasCallback = true;
			}
		});
		return hasCallback;
	}
	
	/**
	 * onRequest(HttpRequest req, HttpResponse res) : void
	 *   Handles requests for remote invocations.
	 */
	var onRequest = function(req, res){
		var method = req.params.method;
		var serviceName = req.params.service;
		var service = _services[serviceName];
		
		var responseHandler = function( obj ){
			var data = JSON.stringify( obj );
			res.setHeader('content-type', 'application/json');
			res.setHeader('content-length', data.length);
			res.statusCode = 200;
			res.end(data);
		};
		
		if( service && (!method || method === '_describe')){
			var results = {
				result : describe(serviceName)
			};
			res.end( JSON.stringify(results) );
		}else if( service && service[method] ){
			var args = req.rawBody ? req.body : [];
			
			var hasCallback = buildCallbackHandlers(args, responseHandler);
			
			var result = service[method].apply(service, args);
			
			if( !hasCallback ){
				var complete = function(){
					responseHandler({result:result});
				}
				if(result && result.on){
					result.on('ready', complete)
				}else{
					complete();
				}
			}
		}else{
			res.end(_('{error: "Service ${service} not found."}', req.params));
		}
	};
	
	exports.register = function(expressApp){
		expressApp.all('/invoker/:service/:method?', onRequest);
	};
	exports.set = function(name, service){
		_services[name] = service;
	};
	exports.get = function(name){
		return _services[name];
	};

})()
